#ifndef _FUNCTION_HELP_H_
#define _FUNCTION_HELP_H_

template <typename R = void>
class CFunction
{
public:
    struct CFuncBase
    {
        virtual ~CFuncBase(){};
        virtual R operator()() = 0;
    };

    static CFuncBase* Bind(R(*f)())
    {
        return new CBindPtr<R(*)(), CArgs0>(f, CArgs0());
    }

    template <typename B1, typename A1>
    static CFuncBase* Bind(R(*f)(B1), A1 a1)
    {
        return new CBindPtr<R(*)(B1), CArgs1<A1>>(f, CArgs1<A1>(a1));
    }

    template <typename B1, typename B2, typename A1, typename A2>
    static CFuncBase* Bind(R(*f)(B1, B2), A1 a1, A2 a2)
    {
        return new CBindPtr<R(*)(B1, B2), CArgs2<A1, A2>>(f, CArgs2<A1, A2>(a1, a2));
    }

    template <typename B1, typename B2, typename B3, typename A1, typename A2, typename A3>
    static CFuncBase* Bind(R(*f)(B1, B2, B3), A1 a1, A2 a2, A3 a3)
    {
        return new CBindPtr<R(*)(B1, B2, B3), CArgs3<A1, A2, A3>>(f, CArgs3<A1, A2, A3>(a1, a2, a3));
    }

    template <typename B1, typename B2, typename B3, typename B4, typename A1, typename A2, typename A3, typename A4>
    static CFuncBase* Bind(R(*f)(B1, B2, B3, B4), A1 a1, A2 a2, A3 a3, A4 a4)
    {
        return new CBindPtr<R(*)(B1, B2, B3, B4), CArgs4<A1, A2, A3, A4>>(f, CArgs4<A1, A2, A3, A4>(a1, a2, a3, a4));
    }

    template <typename B1, typename B2, typename B3, typename B4, typename B5, typename A1, typename A2, typename A3, typename A4, typename A5>
    static CFuncBase* Bind(R(*f)(B1, B2, B3, B4, B5), A1 a1, A2 a2, A3 a3, A4 a4, A5 a5)
    {
        return new CBindPtr<R(*)(B1, B2, B3, B4, B5), CArgs5<A1, A2, A3, A4, A5>>(f, CArgs5<A1, A2, A3, A4, A5>(a1, a2, a3, a4, a5));
    }

    template <typename B1, typename B2, typename B3, typename B4, typename B5, typename B6, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
    static CFuncBase* Bind(R(*f)(B1, B2, B3, B4, B5, B6), A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6)
    {
        return new CBindPtr<R(*)(B1, B2, B3, B4, B5, B6), CArgs6<A1, A2, A3, A4, A5, A6>>(f, CArgs6<A1, A2, A3, A4, A5, A6>(a1, a2, a3, a4, a5, a6));
    }

    template <typename B1, typename B2, typename B3, typename B4, typename B5, typename B6, typename B7, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
    static CFuncBase* Bind(R(*f)(B1, B2, B3, B4, B5, B6, B7), A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7)
    {
        return new CBindPtr<R(*)(B1, B2, B3, B4, B5, B6, B7), CArgs7<A1, A2, A3, A4, A5, A6, A7>>(f, CArgs7<A1, A2, A3, A4, A5, A6, A7>(a1, a2, a3, a4, a5, a6, a7));
    }

    template <typename B1, typename B2, typename B3, typename B4, typename B5, typename B6, typename B7, typename B8, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
    static CFuncBase* Bind(R(*f)(B1, B2, B3, B4, B5, B6, B7, B8), A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8)
    {
        return new CBindPtr<R(*)(B1, B2, B3, B4, B5, B6, B7, B8), CArgs8<A1, A2, A3, A4, A5, A6, A7, A8>>(f, CArgs8<A1, A2, A3, A4, A5, A6, A7, A8>(a1, a2, a3, a4, a5, a6, a7, a8));
    }

    template <typename B1, typename B2, typename B3, typename B4, typename B5, typename B6, typename B7, typename B8, typename B9, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
    static CFuncBase* Bind(R(*f)(B1, B2, B3, B4, B5, B6, B7, B8, B9), A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9)
    {
        return new CBindPtr<R(*)(B1, B2, B3, B4, B5, B6, B7, B8, B9), CArgs9<A1, A2, A3, A4, A5, A6, A7, A8, A9>>(f, CArgs9<A1, A2, A3, A4, A5, A6, A7, A8, A9>(a1, a2, a3, a4, a5, a6, a7, a8, a9));
    }

    template <typename T>
    static CFuncBase* Bind(R(T::*f)(), T* t)
    {
        return new CBindObj<R(T::*)(), T, CArgs0>(f, t, CArgs0());
    }

    template <typename T, typename B1, typename A1>
    static CFuncBase* Bind(R(T::*f)(B1), T* t, A1 a1)
    {
        return new CBindObj<R(T::*)(B1), T, CArgs1<A1>>(f, t, CArgs1<A1>(a1));
    }

    template <typename T, typename B1, typename B2, typename A1, typename A2>
    static CFuncBase* Bind(R(T::*f)(B1, B2), T* t, A1 a1, A2 a2)
    {
        return new CBindObj<R(T::*)(B1, B2), T, CArgs2<A1, A2>>(f, t, CArgs2<A1, A2>(a1, a2));
    }

    template <typename T, typename B1, typename B2, typename B3, typename A1, typename A2, typename A3>
    static CFuncBase* Bind(R(T::*f)(B1, B2, B3), T* t, A1 a1, A2 a2, A3 a3)
    {
        return new CBindObj<R(T::*)(B1, B2, B3), T, CArgs3<A1, A2, A3>>(f, t, CArgs3<A1, A2, A3>(a1, a2, a3));
    }

    template <typename T, typename B1, typename B2, typename B3, typename B4, typename A1, typename A2, typename A3, typename A4>
    static CFuncBase* Bind(R(T::*f)(B1, B2, B3, B4), T* t, A1 a1, A2 a2, A3 a3, A4 a4)
    {
        return new CBindObj<R(T::*)(B1, B2, B3, B4), T, CArgs4<A1, A2, A3, A4>>(f, t, CArgs4<A1, A2, A3, A4>(a1, a2, a3, a4));
    }

    template <typename T, typename B1, typename B2, typename B3, typename B4, typename B5, typename A1, typename A2, typename A3, typename A4, typename A5>
    static CFuncBase* Bind(R(T::*f)(B1, B2, B3, B4, B5), T* t, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5)
    {
        return new CBindObj<R(T::*)(B1, B2, B3, B4, B5), T, CArgs5<A1, A2, A3, A4, A5>>(f, t, CArgs5<A1, A2, A3, A4, A5>(a1, a2, a3, a4, a5));
    }

    template <typename T, typename B1, typename B2, typename B3, typename B4, typename B5, typename B6, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
    static CFuncBase* Bind(R(T::*f)(B1, B2, B3, B4, B5, B6), T* t, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6)
    {
        return new CBindObj<R(T::*)(B1, B2, B3, B4, B5, B6), T, CArgs6<A1, A2, A3, A4, A5, A6>>(f, t, CArgs6<A1, A2, A3, A4, A5, A6>(a1, a2, a3, a4, a5, a6));
    }

    template <typename T, typename B1, typename B2, typename B3, typename B4, typename B5, typename B6, typename B7, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
    static CFuncBase* Bind(R(T::*f)(B1, B2, B3, B4, B5, B6, B7), T* t, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7)
    {
        return new CBindObj<R(T::*)(B1, B2, B3, B4, B5, B6, B7), T, CArgs7<A1, A2, A3, A4, A5, A6, A7>>(f, t, CArgs7<A1, A2, A3, A4, A5, A6, A7>(a1, a2, a3, a4, a5, a6, a7));
    }

    template <typename T, typename B1, typename B2, typename B3, typename B4, typename B5, typename B6, typename B7, typename B8, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
    static CFuncBase* Bind(R(T::*f)(B1, B2, B3, B4, B5, B6, B7, B8), T* t, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8)
    {
        return new CBindObj<R(T::*)(B1, B2, B3, B4, B5, B6, B7, B8), T, CArgs8<A1, A2, A3, A4, A5, A6, A7, A8>>(f, t, CArgs8<A1, A2, A3, A4, A5, A6, A7, A8>(a1, a2, a3, a4, a5, a6, a7, a8));
    }

    template <typename T, typename B1, typename B2, typename B3, typename B4, typename B5, typename B6, typename B7, typename B8, typename B9, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
    static CFuncBase* Bind(R(T::*f)(B1, B2, B3, B4, B5, B6, B7, B8, B9), T* t, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9)
    {
        return new CBindObj<R(T::*)(B1, B2, B3, B4, B5, B6, B7, B8, B9), T, CArgs9<A1, A2, A3, A4, A5, A6, A7, A8, A9>>(f, t, CArgs9<A1, A2, A3, A4, A5, A6, A7, A8, A9>(a1, a2, a3, a4, a5, a6, a7, a8, a9));
    }
private:
    struct CArgs0
    {
        template <typename F, typename T>
        R operator()(F f, T* t) const { return (t->*f)(); }

        template <typename F>
        R operator()(F f) const { return f(); }
    };

    template <typename A1>
    struct CArgs1: public CArgs0
    {
        CArgs1(const A1& a1): arg1(a1) {}
        A1 arg1;

        template <typename F, typename T>
        R operator()(F f, T* t) const { return (t->*f)(arg1); }

        template <typename F>
        R operator()(F f) const { return f(arg1); }
    };

    template <typename A1, typename A2>
    struct CArgs2: public CArgs1<A1>
    {
        CArgs2(A1 a1, A2 a2): CArgs1(a1), arg2(a2) {}
        A2 arg2;

        template <typename F, typename T>
        R operator()(F f, T* t) const { return (t->*f)(arg1, arg2); }

        template <typename F>
        R operator()(F f) const { return f(arg1, arg2); }
    };

    template <typename A1, typename A2, typename A3>
    struct CArgs3: public CArgs2<A1, A2>
    {
        CArgs3(A1 a1, A2 a2, A3 a3): CArgs2(a1, a2), arg3(a3) {}
        A3 arg3;

        template <typename F, typename T>
        R operator()(F f, T* t) const { return (t->*f)(arg1, arg2, arg3); }

        template <typename F>
        R operator()(F f) const { return f(arg1, arg2, arg3); }
    };

    template <typename A1, typename A2, typename A3, typename A4>
    struct CArgs4: public CArgs3<A1, A2, A3>
    {
        CArgs4(A1 a1, A2 a2, A3 a3, A4 a4): CArgs3(a1, a2, a3), arg4(a4) {}
        A4 arg4;

        template <typename F, typename T>
        R operator()(F f, T* t) const { return (t->*f)(arg1, arg2, arg3, arg4); }

        template <typename F>
        R operator()(F f) const { return f(arg1, arg2, arg3, arg4); }
    };

    template <typename A1, typename A2, typename A3, typename A4, typename A5>
    struct CArgs5: public CArgs4<A1, A2, A3, A4>
    {
        CArgs5(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5): CArgs4(a1, a2, a3, a4), arg5(a5) {}
        A5 arg5;

        template <typename F, typename T>
        R operator()(F f, T* t) const { return (t->*f)(arg1, arg2, arg3, arg4, arg5); }

        template <typename F>
        R operator()(F f) const { return f(arg1, arg2, arg3, arg4, arg5); }
    };

    template <typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
    struct CArgs6: public CArgs5<A1, A2, A3, A4, A5>
    {
        CArgs6(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6): CArgs5(a1, a2, a3, a4, a5), arg6(a6) {}
        A6 arg6;

        template <typename F, typename T>
        R operator()(F f, T* t) const { return (t->*f)(arg1, arg2, arg3, arg4, arg5, arg6); }

        template <typename F>
        R operator()(F f) const { return f(arg1, arg2, arg3, arg4, arg5, arg6); }
    };

    template <typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
    struct CArgs7: public CArgs6<A1, A2, A3, A4, A5, A6>
    {
        CArgs7(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7): CArgs6(a1, a2, a3, a4, a5, a6), arg7(a7) {}
        A7 arg7;

        template <typename F, typename T>
        R operator()(F f, T* t) const { return (t->*f)(arg1, arg2, arg3, arg4, arg5, arg6, arg7); }

        template <typename F>
        R operator()(F f) const { return f(arg1, arg2, arg3, arg4, arg5, arg6, arg7); }
    };

    template <typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
    struct CArgs8: public CArgs7<A1, A2, A3, A4, A5, A6, A7>
    {
        CArgs8(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8): CArgs7(a1, a2, a3, a4, a5, a6, a7), arg8(a8) {}
        A8 arg8;

        template <typename F, typename T>
        R operator()(F f, T* t) const { return (t->*f)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); }

        template <typename F>
        R operator()(F f) const { return f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); }
    };

    template <typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
    struct CArgs9: public CArgs8<A1, A2, A3, A4, A5, A6, A7, A8>
    {
        CArgs9(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9): CArgs8(a1, a2, a3, a4, a5, a6, a7, a8), arg9(a9) {}
        A9 arg9;

        template <typename F, typename T>
        R operator()(F f, T* t) const { return (t->*f)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); }

        template <typename F>
        R operator()(F f) const { return f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); }
    };
private:
    template <typename F, typename A>
    class CBindPtr: public CFuncBase
    {
    public:
        CBindPtr(F f, A a):func(f),args(a) {}
        R operator()() { return args(func); }
    private:
        F func;
        A args;
    };

    template <typename F, typename T, typename A>
    class CBindObj: public CFuncBase
    {
    public:
        CBindObj(F f, T* t, A a):func(f),obj(t),args(a) {}
        R operator()() { return args(func, obj); }
    private:
        F func;
        T* obj;
        A args;
    };
};

#endif // _FUNCTION_HELP_H_